Кратко пробежаться по плану, подчеркнуть, что лекция делится на две большие части — Windows (WinAPI/GDI) и Linux (Cairo/GTK/Qt), и завершается кроссплатформенными решениями. Спросить, кто уже работал с WinAPI или Cairo.
Подчеркнуть, что ресурсы компилируются в .exe/.dll и не требуют отдельных файлов при распространении. Обратить внимание на строковые таблицы — это основа локализации (i18n). Спросить, зачем хранить строки в ресурсах, а не прямо в коде.
Показать, что rc-файл обрабатывается компилятором ресурсов (rc.exe) и генерирует .res, который линкуется в exe. Упомянуть, что для Linux-приложений аналогом служат embedded resources (Qt .qrc, GLib GResource).
Объяснить MAKEINTRESOURCE — это макрос, преобразующий числовой ID в LPCTSTR. Напомнить, что каждый загруженный GDI-объект нужно освобождать (DeleteObject). Типичная ошибка — утечка ресурсов при повторной загрузке.
Обратить внимание на амперсанд (&) — он задаёт мнемоническую клавишу (Alt+буква). Спросить, чем системное меню отличается от пользовательского (можно добавлять свои пункты через AppendMenu).
Показать, что \t в строке меню — это табуляция, которая визуально выравнивает сочетание клавиш вправо. Акцентировать внимание на SEPARATOR и соглашении об именовании ID (префикс по группе: ID_FILE_, ID_EDIT_).
Указать, что программное создание полезно для динамических меню (например, список последних файлов). Отметить, что AppendMenu считается устаревшим — лучше использовать InsertMenuItem. Напомнить про необходимость DestroyMenu при динамическом создании.
Ключевой момент: ВСЕ элементы меню (и кнопки, и акселераторы) генерируют WM_COMMAND. LOWORD(wParam) — идентификатор, HIWORD(wParam) — код уведомления. Спросить, как отличить команду меню от нажатия кнопки (по HIWORD: для меню — 0, для кнопок — BN_CLICKED).
Спросить студентов, приведут ли они примеры модальных/немодальных диалогов из известных программ. Подчеркнуть, что модальный диалог имеет свой цикл сообщений. Обратить внимание на UX: чрезмерное использование модальных диалогов раздражает пользователей.
Объяснить параметры DIALOGEX: x, y, width, height — в диалоговых единицах (DLU), а не пикселях. DEFPUSHBUTTON — кнопка по умолчанию (нажимается Enter). IDC_STATIC — специальный ID для статических элементов (не обрабатывается в коде).
DialogBox — блокирующий вызов, создаёт собственный цикл сообщений. CreateDialog — возвращает HWND немедленно, сообщение WM_INITDIALOG приходит асинхронно. Частая ошибка: забывать вызывать ShowWindow для немодального диалога.
Отличие от WndProc: возвращает BOOL (TRUE/FALSE), а не LRESULT. WM_INITDIALOG вместо WM_CREATE. EndDialog — аналог DestroyWindow для модальных диалогов. Спросить, что вернёт DialogBox, если закрыть через крестик (IDCANCEL).
Подчеркнуть, что GDI обеспечивает аппаратную независимость — приложение рисует одинаково на экране и принтере. Упомянуть эволюцию: GDI → GDI+ → Direct2D. GDI устарел, но всё ещё используется и важен для понимания основ.
Все объекты GDI (кроме DC) имеют тип HGDIOBJ и должны удаляться через DeleteObject. Напомнить паттерн: SelectObject → рисование → SelectObject(old) → DeleteObject. Спросить, к какому типу относится палитра (Palette).
Важное правило: GetDC/ReleaseDC — пара. Полученный DC нужно обязательно освободить. В отличие от GetDC, BeginPaint/EndPaint вызываются ТОЛЬКО в обработчике WM_PAINT. Спросить, что будет, если рисовать вне WM_PAINT (рисунок пропадёт при перерисовке).
WM_PAINT генерируется только когда система определяет, что часть окна нужна в перерисовке. BeginPaint валидирует обновлённую область. Если WM_PAINT не обработан — система будет отправлять его бесконечно. Спросить, как принудительно вызвать перерисовку (InvalidateRect / UpdateWindow).
Подчеркнуть паттерн сохранения/восстановления: SelectObject возвращает старый объект, его нужно сохранить и восстановить перед DeleteObject. Штриховые стили работают только при толщине 1 пиксель — частая ошибка студентов.
Важно: объекты, полученные через GetStockObject, НЕ нужно удалять (DeleteObject для них не вызывается). Это частая ошибка. Кисть определяет заливку замкнутых фигур, перо — обводку.
Обратить внимание: для Rectangle и Ellipse координаты задают ограничивающий прямоугольник, а не центр. Arc использует углы в десятых долях градуса (xStart, yStart — начальная точка, xEnd, yEnd — конечная). Все фигуры используют текущие перо и кисть из DC.
CreateFont имеет 14 параметров — больше, чем у большинства функций WinAPI. Упомянуть более удобную CreateFontIndirect (принимает структуру LOGFONT). Обратить внимание на высоту шрифта: отрицательное значение — высота символа, положительное — высота ячейки.
Ключевое различие: WM_KEYDOWN — виртуальный код клавиши (VK_), не зависит от раскладки. WM_CHAR — символьный код, зависит от языка ввода. WM_KEYDOWN → WM_CHAR — последовательность для печатных символов. Спросить, для чего лучше использовать WM_KEYDOWN (управление, игра), а для чего WM_CHAR (ввод текста).
Подчеркнуть, что координаты мыши в lParam — клиентские (относительно окна), а не экранные. Для экранных координат — ClientToScreen. Чтобы получать WM_LBUTTONDBLCLK, необходимо зарегистрировать класс окна с CS_DBLCLKS. Частая ошибка: использовать LOWORD/HIWORD вместо GET_X_LPARAM/GET_Y_LPARAM (проблемы на multi-monitor).
Это один из важнейших слайдов лекции. Подчеркнуть: рисуем в памяти → одним вызовом BitBlt копируем на экран. Без буферизации — мерцание, т.к. каждая операция рисования сразу видна на экране. SRCCOPY — режим копирования без трансформации. Спросить, какие ещё есть ROP-коды (NOTSRCERASE, SRCAND, SRCINVERT).
LoadImage поддерживает BMP, ICO, CUR. Для других форматов (PNG, JPEG) нужна GDI+ или сторонняя библиотека (stb_image, libpng). StretchBlt позволяет масштабировать, но без антиалиасинга — для качественного масштабирования нужен GDI+.
Это объёмный слайд — не застревать на синтаксисе каждого примера. Главное: Cairo — низкоуровневая библиотека для 2D-графики (используется внутри GTK). GTK — высокоуровневый тулкит. Qt/QPainter — объектно-ориентированный подход (наиболее привычный для C++-разработчиков). Сравнить с WinAPI GDI: Cairo использует состояние (state machine), GDI — объекты в DC.
Ключевой вывод лекции: выбор технологии зависит от задачи. SDL — не GUI-тулkit, а низкоуровневый доступ к окну и вводу (игры, эмуляторы). Для десктопных приложений с виджетами — Qt или GTK. Упомянуть Wayland как замену X11 — это актуально для современного Linux.
Этот раздел — обзор, не углубляемся в синтаксис. Главное: понять, что существует иерархия графических API — от высокоуровневых (GDI, Cairo) до низкоуровневых (Vulkan, DirectX 12, Metal). Низкоуровневые дают больше контроля и производительности, но требуют значительно больше кода. Для сравнения: «Hello Triangle» на GDI — ~30 строк, на Vulkan — ~1000 строк.
Пробежаться по пунктам, попросить студентов назвать, какой раздел показался наиболее сложным. Вернуться к плану лекции и убедиться, что все темы покрыты.
Рекомендовать студентам ответить на вопросы устно в парах. Вопросы 3, 5 и 7 — наиболее проблемные, обсудить их подробно, если останется время.
Задания рассчитаны на лабораторные работы. Задание 1 — самое базовое. Задание 2 — можно совмещать с заданием 1 (добавить Drawing Area). Задания 3 и 4 — для работы в Linux. Напомнить про компиляцию: pkg-config для Cairo/GTK, sdl2-config для SDL.